home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / drdobbs / 1988 / 12 / scroll.asc < prev    next >
Text File  |  1988-12-31  |  48KB  |  1,335 lines

  1. _SMOOTH SCROLLING AND PANNING_
  2. by Andrew Chalk
  3.  
  4. [LISTING 1]
  5.  
  6. /*
  7. Copyright (C) Magna Carta Software, 1988.  All Rights Reserved.
  8.  
  9. SMOOTH BROWSE -- A file browser to illustrate how to do smooth scrolling and
  10. panning on the EGA and the VGA.
  11. SYSTEM REQUIREMENTS:  EGA or VGA, Enhanced Color or Monochrome Display.
  12. COMPILER SUPPORT: Turbo C v1.5,  MSC v5.0/5.1/Quick C,  Mix Power C v1.1
  13. WATCOM C v6.0.
  14. Simple compiler invocation (suggested):
  15.     TURBO:          "TCC sb.c" (from the environment).
  16.     MSC:            "CL /AS sb.c"
  17.     MIX POWER C:    "PC /E sb.c"
  18.     WATCOM C:       "WCC sb 3"
  19.                     "WLINK FILE sb LIB ?:\watcomc\lib\clib%2, ?:\watcomc\lib\math%2
  20.                     OPTION Map, caseexact, stack=2048"
  21. Usage: UpArrow -- scroll up, DnArrow -- scroll down, + scroll faster,
  22. - scroll slower, RtArrow -- smooth pan right, LtArrow -- smooth pan left
  23. Space -- pause scrolling, PgUp, PgDn, Ctrl PgUp, Ctrl PgDn
  24. First version: 3/5/88.  Last update 06-07-88.
  25. */
  26.  
  27.  
  28. /* MSC SPECIFIC CODE */
  29. #if !defined(__TURBOC__)        /* these two manifest constants are         */
  30. #if !defined(__POWERC)          /* #defined by the respective compilers.    */
  31. #if !defined(__WATCOMC__)           /* this one is our invention                */
  32.                                 /* Fall through... must be MSC!             */
  33.  
  34. /* MSC _dos_findfirst structure compatible with TURBOC "findfirst" function */
  35.  
  36. struct find_t {
  37.     char ff_reserved[21];
  38.     char ff_attrib;
  39.     unsigned ff_ftime;
  40.     unsigned ff_fdate;
  41.     long ff_fsize;
  42.     char ff_name[13];
  43. };
  44. #define _FIND_T_DEFINED  /* A Microsoft manifest constant to prevent redef. */
  45.  
  46. #define ffblk find_t
  47. #define findfirst(x,y,z) _dos_findfirst(x,z,y)  /* note parm. order! */
  48. #define bioskey(x) _bios_keybrd(x)
  49.  
  50. #define inportb(port) inp(port)
  51. #define outportb(port,value) outp((port),(value))
  52. #define outport(port,value) outpw((port),(value))
  53. #define MK_FP(seg,ofs)  ((void far *)((((unsigned long)(seg)) << 16) | (ofs)))
  54. #define peekb(a,b) (*((char far*)MK_FP((a),(b))))
  55. #endif
  56. #endif
  57. #endif
  58.  
  59. /* WATCOM C SPECIFIC CODE */
  60. #if defined(__WATCOMC__)
  61.     #include <conio.h>
  62.     #include <sys\types.h>
  63.     #include <direct.h>
  64.  
  65.     #define inportb(port)           inp(port)
  66.     #define outportb(port,value)    outp((port),(value))
  67.     #define outport(port,value)     outpw((port),(value))
  68.     #define peekb(a,b) (*((char far*)MK_FP((a),(b))))
  69.     #define bioskey(x) kbhit()
  70.  
  71.     struct ffblk {
  72.         char ff_reserved[21];
  73.         char ff_attrib;
  74.         unsigned ff_ftime;
  75.         unsigned ff_fdate;
  76.         long ff_fsize;
  77.         char ff_name[13];
  78.     };
  79.     int findfirst(const char *pathname, struct ffblk *ffblk, int attrib);
  80.  
  81. #endif
  82.  
  83. /* OTHER COMPILER-SPECIFIC CODE */
  84. #if defined(__TURBOC__)
  85.     #include <dir.h>
  86. #elif !defined(__TURBOC__) && !defined(__WATCOMC__)
  87.     #include <conio.h>
  88.     #include <direct.h>
  89. #endif
  90.  
  91. #if !defined(__WATCOMC__)
  92.     #include <bios.h>
  93. #endif
  94.  
  95. #include <dos.h>
  96. #include <process.h>
  97. #include <stdarg.h>
  98. #include <stdio.h>
  99. #include <stdlib.h>
  100.  
  101. typedef unsigned char BYTE;     /* A convenient new data type */
  102.  
  103. /* MANIFEST CONSTANTS */
  104. #define VERSION "1.0"
  105. #define LOGICAL_WIDTH 132   /* EGA/VGA logical screen width (bytes, max. 512) */
  106. #define MAXFILESIZE 0XFFFF  /* Maximum permisable file size to load */
  107. #define NEWCOLOR    0X8     /* Code for new EGA color to load       */
  108. #define BCOLOR      0       /* Screen background color              */
  109. #define FCOLOR      15      /* Screen foreground color              */
  110. #define TRUE        1
  111. #define FALSE       !TRUE
  112. #define TABSIZE     4
  113.  
  114. #define _KEYBRD_READY 1
  115.  
  116. #define KEYBOARD    0X16    /* The BIOS keyboard interrupt number */
  117. #define VIDEO       0X10    /* The BIOS video interrupt number */
  118.  
  119. /* KEY DEFINITIONS */
  120. #define ESC         1
  121. #define UP_ARROW    72
  122. #define DOWN_ARROW  80
  123. #define LEFT_ARROW  75
  124. #define RIGHT_ARROW 77
  125. #define PAGE_UP     73
  126. #define PAGE_DOWN   81
  127. #define PLUS        78
  128. #define MINUS       74
  129. #define CPAGE_UP    132
  130. #define CPAGE_DOWN  118
  131. #define SPACEBAR    57
  132.  
  133. /* EGA AND VGA REGISTER VALUES */
  134. #define PRESET_ROW_SCAN     8     /* Address of preset row scan reg. of CRTC */
  135. #define START_ADDRESS_HIGH  0X0C  /* Address of start address h reg. of CRTC */
  136. #define START_ADDRESS_LOW   0X0D  /* Address of start address l reg. of CRTC */
  137. #define AC_INDEX            0X3C0 /* Attribute Controller Index Register     */
  138. #define AC_HPP              0X13 | 0X20   /* Horizontal Pel Panning Register */
  139. #define AC_MCR              0X10  /* Attribute Controller Mode Control Reg.  */
  140. #define LINE_COMPARE        0X18  /* CRTC line compare register.             */
  141. #define CRTC_OVERFLOW       0X07  /* CRTC overflow register.                 */
  142. #define MAX_SCAN_LINE       0X09  /* CRTC maximum scan line register.        */
  143.  
  144. /* GLOBAL VARIABLES */
  145. struct ffblk u_file;         /* DOS file descriptor returned by findfirst() */
  146. FILE *p_u_file;              /* Ptr. to the user file to browse */
  147. unsigned end_of_file, screen_size, end_of_buffer, buffer_size;
  148. unsigned right_edge, left_edge; /* TRUE if we are at the edge of the screen */
  149. int vpel, hpel, mode, mono;
  150.  
  151. /* The following three structures are a convenient form in which to store   */
  152. /* video information and you can extend them with more information.         */
  153. /* We will declare one of each type (below).                                */
  154.  
  155. struct video_descriptor {
  156.     unsigned ev_active;             /* if TRUE, an EGA or VGA is active */
  157.     unsigned color;                 /* if TRUE, ega drives color monitor */
  158.     unsigned ecd;                   /* if TRUE, Enhanced Color Display */
  159.     unsigned ram;                   /* size of EGA memory (in k) */
  160.     BYTE char_ht, char_wi; /* character height and width (EGA/VGA) */
  161.     BYTE far *base;        /* starting address of the video buffer */
  162.     unsigned address;               /* starting address of the active video page */
  163.     unsigned isr;                   /* EGA/VGA input status register address */
  164.     unsigned crtc;                  /* CRT Controller Register address */
  165. };
  166.  
  167. struct screen_descriptor {
  168.     unsigned rows;
  169.     unsigned cols;
  170.     unsigned a_start;      /* start address of screen A in split screen mode */
  171.     unsigned logical_width;     /* screen logical width in words. max: 0x100 */
  172.     unsigned start_addr;        /* screen start address. max: 0X4000 */
  173. };
  174.  
  175. struct enhanced_graphics_adapter {
  176.     unsigned present;               /* if TRUE, EGA present */
  177.     unsigned active;                /* if TRUE, EGA active */
  178. };
  179.  
  180. struct video_graphics_array {
  181.     unsigned present;               /* if TRUE, VGA present */
  182.     unsigned active;                /* if TRUE, EGA present */
  183.  
  184. };
  185.  
  186. struct video_descriptor video;
  187. struct screen_descriptor screen;
  188. struct enhanced_graphics_adapter ega;
  189. struct video_graphics_array vga;
  190.  
  191.  
  192. /* FUNCTION PROTOTYPES -- In order of appearance */
  193. int             main_menu(BYTE *p_fbuf);
  194. void            intro_to_buffer(unsigned state);
  195. BYTE * build_line(BYTE **p_fbuf);
  196. int             line_to_buffer(BYTE *p_fbuf);
  197. void            smooth_scroll(int count, unsigned speed);
  198. int             smooth_pan(int count, unsigned speed);
  199. unsigned        set_logical_screen_width(unsigned l_width);
  200. void            set_start_addr(unsigned start_addr);
  201. void            set_pel_pan(int hpel);
  202. void            set_line_compare(unsigned scan_line);
  203. void            load_ega_color(BYTE pregister, BYTE color);
  204. unsigned        set_video_mode(BYTE m);
  205. int             get_v_mode(void);
  206. void            get_v_config(void);
  207. unsigned        get_key(void);
  208. int             get_cursor_size(void);
  209. void            set_cursor_size(BYTE top_line, BYTE bottom_line);
  210. void            error(char *fs,...);
  211.  
  212.  
  213. int main(int argc, char *argv[])
  214. {
  215.  
  216.     BYTE *p_fbuf;
  217.     int not_found, rc;
  218.     int csize;
  219.  
  220.  
  221.     /* TEST FOR A FILENAME/PATH */
  222.     /* No filename entered -- give syntax message and exit */
  223.     if(argc < 2) {
  224.         fprintf(stderr,"Smooth browser version %s by\n\tMagna Carta Software\n\tP.O. Box 475594\n\tGarland, Texas 75047\n", VERSION);
  225.         fprintf(stderr, "Usage: sb pathname\n");
  226.         exit(1);
  227.     }
  228.  
  229.     /* SEE IF FILE EXISTS */
  230.     not_found = findfirst(argv[1],&u_file,0);
  231.     if (not_found) error("\nFile %s not found.",argv[1]);
  232.  
  233.     /* get file size. If too large (> MAXFILESIZE) then tell the user and exit */
  234.     if (u_file.ff_fsize > MAXFILESIZE)
  235.         error("\nMaximum file size %u bytes exceeded\n",MAXFILESIZE);
  236.  
  237.     /* TEST FOR AN ACCEPTABLE VIDEO CONFIGURATION */
  238.     mode = get_v_mode();
  239.     get_v_config();
  240.     if (!vga.present && !ega.present)
  241.         error("EGA/VGA required to run SMOOTH BROWSE");
  242.     if (!video.ev_active)
  243.         error("The EGA/VGA must be active to use SMOOTH BROWSE");
  244.     if (!video.ecd && video.color)
  245.         error("Enhanced Color or Monochrome Display required to run SMOOTH BROWSE");
  246.     if (video.ram < 128)
  247.         error("More video RAM required to use SMOOTH BROWSE");
  248.  
  249.  
  250.     /* CREATE BUFFER FOR FILE. IF ERROR, RETURN AN ERRORLEVEL TO DOS */
  251.     p_fbuf = (BYTE *) calloc(1, (unsigned) u_file.ff_fsize);
  252.     if (p_fbuf == NULL) error("Not enough memory available to load file");
  253.  
  254.     /* OPEN FILE. IF THERE IS AN ERROR RETURN AN ERRORLEVEL TO DOS */
  255.     p_u_file = fopen(argv[1], "rt");
  256.     if (p_u_file == NULL) error("Error opening file %s",u_file.ff_name);
  257.  
  258.     /* READ IN FILE. IF THERE IS A SHORT COUNT RETURN AN ERRORLEVEL TO DOS */
  259.     if (fread(p_fbuf,1,(int) u_file.ff_fsize,p_u_file) == NULL)
  260.         error("Short count returned when reading file");
  261.  
  262.     /* SYSTEM CHECKS OUT AND FILE EXISTS AND IS WITHIN SIZE LIMITS */
  263.     /* NOTE: VARIABLE MONO IS SET BY THE FUNCTION GET_V_MODE() */
  264.     if (mode != 7) set_video_mode(3);
  265.     else set_video_mode(7);
  266.  
  267.     /* GET THE ADDRESS OF THE INPUT STATUS REGISTER AND CRT CONTROLLER */
  268.     if (mono) video.isr = 0x03ba;
  269.     else video.isr = 0x03da;
  270.     video.crtc = video.isr - 6;
  271.  
  272.     /* SAVE THE CURSOR AND TURN IT OFF */
  273.     csize = get_cursor_size();
  274.     set_cursor_size(0,0);
  275.  
  276.     /* Load a pleasant but unusual EGA color */
  277.     load_ega_color(0,NEWCOLOR);
  278.  
  279.  
  280.     /* DO IT */
  281.     rc = main_menu(p_fbuf);
  282.  
  283.     /* EXIT ROUTINES -- RESTORE THE CURSOR AND RESET THE VIDEO MODE. */
  284.     set_cursor_size((BYTE) (csize >> 8), (BYTE) csize);
  285.     if (mono) set_video_mode(7);
  286.     else set_video_mode(3);
  287.     return(rc);
  288. }
  289.  
  290.  
  291.  
  292. int main_menu(BYTE *p_fbuf)
  293. {
  294.     unsigned speed=1;
  295.     int v_direction=0, old_v_direction=0;
  296.     int h_direction=0, old_h_direction=0;
  297.     unsigned scan;
  298.     BYTE old_reg;
  299.     int ret;
  300.  
  301.     /* SET LOGICAL SCREEN WIDTH */
  302.     screen.logical_width = LOGICAL_WIDTH;
  303.     set_logical_screen_width(screen.logical_width);
  304.  
  305.     /* SPLIT THE SCREEN */
  306.     screen.a_start = screen.logical_width;
  307.     set_line_compare((screen.rows - 1) * video.char_ht);
  308.  
  309.  
  310.     /* CREATE 16 VIDEO PAGES IN VIDEO MODE 3 -- MANY CAVEATS TO THIS */
  311.     outportb(0X3CE, 6);                 /* Point index at Misc. register */
  312.     if (vga.active) old_reg = (BYTE) inportb(0X3CF); /* save contents */
  313.     else old_reg = 0X0E;
  314.     outportb(0X3CF, old_reg & 0XF7);    /* reset EGA ram to A0000-AFFFF */
  315.     video.base = (BYTE far *) 0XA0000000L;
  316.  
  317.     /* SET SCREEN START ADDRESS FOR FILE */
  318.     screen.start_addr = screen.a_start;
  319.     set_start_addr(screen.start_addr);
  320.     screen_size = screen.logical_width*(screen.rows-1);
  321.  
  322.     /* DISPLAY THE INTRODUCTORY MESSAGE */
  323.     intro_to_buffer(TRUE);
  324.  
  325.     /* DISPLAY THE FILE */
  326.     line_to_buffer(p_fbuf);             /* position file on screen */
  327.     end_of_buffer = end_of_file/2 - screen_size;
  328.     inportb(video.isr); /* a dummy read to initialize the Attribute Controller */
  329.  
  330.  
  331.     left_edge = TRUE;   /* file display starts at left edge */
  332.  
  333.     /* THIS IS THE MAIN LOOP THAT MOVES LOGICAL SCREEN 'A' AROUND */
  334.     do {
  335.         do {
  336.             if (v_direction) {
  337.                 smooth_scroll(v_direction,speed);
  338.                 old_v_direction = old_h_direction = 0;
  339.             }
  340.             else if (h_direction) {
  341.                 ret = smooth_pan(h_direction,speed);
  342.                 if (ret == -1) h_direction = v_direction = 0;
  343.                 old_h_direction = old_v_direction = 0;
  344.             }
  345.         } while (!bioskey(_KEYBRD_READY));
  346.  
  347.         scan = get_key();
  348.         switch(scan) {
  349.             case  ESC:      /* The user's key presses tell us what to do */
  350.                  break;
  351.  
  352.             case UP_ARROW:
  353.                 v_direction = -2;
  354.                 h_direction = 0;
  355.                 break;
  356.  
  357.             case DOWN_ARROW:
  358.                 v_direction = 2;
  359.                 h_direction = 0;
  360.                 break;
  361.  
  362.             case LEFT_ARROW:
  363.                 intro_to_buffer(FALSE);
  364.                 h_direction = -1;
  365.                 v_direction = 0;
  366.                 break;
  367.  
  368.             case RIGHT_ARROW:
  369.                 intro_to_buffer(FALSE);
  370.                 h_direction = 1;
  371.                 v_direction = 0;
  372.                 break;
  373.  
  374.             case PAGE_UP:
  375.                 if (screen.start_addr > screen_size + screen.logical_width) screen.start_addr -= screen_size;
  376.                 else {
  377.                     screen.start_addr = screen.a_start;
  378.                     left_edge = TRUE;
  379.                 }
  380.                 vpel = 0;       /* Set the preset row scan register to 0 */
  381.                 /* address the preset row scan register */
  382.                 outport(video.crtc, (vpel << 8) | PRESET_ROW_SCAN);
  383.                 set_start_addr(screen.start_addr);
  384.                 break;
  385.  
  386.             case PAGE_DOWN:                                 /* pg dn */
  387.                 if (screen.start_addr < end_of_buffer - screen_size)
  388.                     screen.start_addr += screen_size;
  389.                 else screen.start_addr = end_of_buffer;
  390.                 vpel = 0;       /* Set the preset row scan register to 0 */
  391.                 outport(video.crtc, (vpel << 8) | PRESET_ROW_SCAN);
  392.  
  393.                 /* reset pel panning position */
  394.                 hpel = (mono || vga.active) ? 8 : 0;
  395.                 set_pel_pan(hpel);
  396.                 set_start_addr(screen.start_addr);
  397.                 break;
  398.  
  399.             case PLUS:                      /* '+' key -- scroll faster */
  400.                 if (speed < 5) speed++;
  401.                 break;
  402.  
  403.             case MINUS:                     /* '-' key -- scroll slower */
  404.                 if (speed > 1) speed--;
  405.                 break;
  406.  
  407.             case SPACEBAR:                  /* space bar -- toggle scrolling */
  408.                 if (v_direction) {          /* moving vertically, so stop */
  409.                     old_v_direction = v_direction;
  410.                     v_direction = 0;
  411.                 }
  412.                 else if (h_direction) {     /* moving horizontally, so stop */
  413.                     old_h_direction = h_direction;
  414.                     h_direction = 0;
  415.                 }
  416.                 else if (old_v_direction) {  /* stopped -- start vertical */
  417.                     v_direction = old_v_direction;
  418.                 }
  419.                 else if (old_h_direction) {  /* stopped -- start horizontal */
  420.                     h_direction = old_h_direction;
  421.                 }
  422.                 break;
  423.  
  424.             case CPAGE_UP:                   /* ctrl-pgup -- go to top */
  425.                 screen.start_addr = screen.a_start;
  426.                 left_edge = TRUE;
  427.  
  428.                 /* reset pel panning position */
  429.                 vpel = 0;       /* Set the preset row scan register to 0 */
  430.                 outport(video.crtc, (vpel << 8) | PRESET_ROW_SCAN);
  431.                 hpel = (mono || vga.active) ? 8 : 0;
  432.                 set_start_addr(screen.start_addr);
  433.                 set_pel_pan(hpel);
  434.                 break;
  435.  
  436.             case CPAGE_DOWN:                 /* ctrl-pgdn -- go to bottom */
  437.                 screen.start_addr = end_of_buffer;
  438.  
  439.                 /* reset pel panning position */
  440.                 vpel = 0;   /* Set the preset row scan register to 0 */
  441.                 outport(video.crtc, (vpel << 8) | PRESET_ROW_SCAN);
  442.                 hpel = (mono || vga.active) ? 8 : 0;
  443.                 set_pel_pan(hpel);
  444.  
  445.                 set_start_addr(screen.start_addr);
  446.                 break;
  447.  
  448.             default:
  449.                 break;
  450.         }
  451.     } while (scan != 1);
  452.  
  453.     return (0);
  454. }
  455.  
  456.  
  457. /* INTRO_TO_BUFFER -- Writes the introductory messsage on screen 'B' */
  458. void intro_to_buffer(unsigned state)
  459. {
  460.     BYTE far *ega_buf ;
  461.     static BYTE a_sbuf[] = "SMOOTH BROWSE v1.0 -- by Magna Carta Software, 1988";
  462.     BYTE *p_sbuf, att;
  463.  
  464.     ega_buf = video.base;
  465.     att = ((FCOLOR & 0X7) << 4) + BCOLOR;
  466.     if (state) {
  467.         p_sbuf = a_sbuf;
  468.         while (*p_sbuf) {
  469.             *ega_buf++ = *p_sbuf++;
  470.             *ega_buf++ = att;
  471.         }
  472.     }
  473.     while (ega_buf < video.base + (screen.logical_width << 1) ) {
  474.         *ega_buf++ = '\x20';
  475.         *ega_buf++ = att;
  476.     }
  477. }
  478.  
  479.  
  480. /* BUILD_LINE -- This function constructs a line of text from data read in  */
  481. /* from the file.                                                           */
  482. BYTE *build_line(BYTE **p_fbuf)
  483. {
  484.     static BYTE lbuf[LOGICAL_WIDTH + 1];/* add 1 for '\0' terminator */
  485.     BYTE *p_lbuf;
  486.     int i;
  487.  
  488.     p_lbuf = lbuf;  /* point to the buffer to hold the line that we build */
  489.  
  490.     /* construct a line in the buffer for display until we hit a '\n' or '\t'*/
  491.     while (**p_fbuf != '\n') {
  492.         if (**p_fbuf == '\t') for(i=0;i<TABSIZE;i++) *p_lbuf++ = 0x20;
  493.         else if (**p_fbuf >= 0x20 && **p_fbuf < 0x80) *p_lbuf++ = **p_fbuf;
  494.         (*p_fbuf)++;
  495.         if (p_lbuf >= lbuf + screen.logical_width) break;
  496.     }
  497.     *p_lbuf = '\0';
  498.     (*p_fbuf)++;        /* advance past the new line character */
  499.  
  500.     return (lbuf);
  501. }
  502.  
  503.  
  504. /* LINE_TO_BUFFER -- Moves the line from memory to the video buffer */
  505. int line_to_buffer(BYTE *p_fbuf)
  506. {
  507.     BYTE far *p_vbuff, far *line, far *eob;
  508.     BYTE *p_lbuf, att;
  509.     unsigned i;
  510.  
  511.     p_vbuff = video.base + 2*screen.logical_width;
  512.     att = (BCOLOR << 4) + FCOLOR;
  513.     for (i = 0; i < 0X8000 - 2*screen.logical_width; i++) {
  514.         p_vbuff[i++] = '\0';
  515.         p_vbuff[i] = att;
  516.     }
  517.     eob = video.base + (0XFFFF - screen.logical_width);
  518.     line = p_vbuff;
  519.     while (*p_fbuf && p_vbuff < eob ) {
  520.         p_lbuf = build_line(&p_fbuf);
  521.         while (*p_lbuf) {
  522.             *p_vbuff++ = *p_lbuf++;
  523.             *p_vbuff++ = att;
  524.         }
  525.         /* go to next screen line */
  526.         line += screen.logical_width*2;
  527.         p_vbuff = line;
  528.     }
  529.     end_of_file = FP_OFF(p_vbuff);
  530.     return (0);
  531. }
  532.  
  533.  
  534. /* SMOOTH_SCROLL scrolls the EGA video buffer the number of scan lines
  535. indicated in "count" at a speed of "speed" scan lines per vertical retrace.
  536. One retrace occurs each 60th of a second regardless of processor speed.
  537. */
  538. void smooth_scroll(int count, unsigned speed)
  539. {
  540.  
  541.     unsigned i;
  542.  
  543.     /* GET THE START ADDRESS OF THE SCREEN BUFFER */
  544.     outportb(video.crtc, START_ADDRESS_HIGH);       /* High byte */
  545.     screen.start_addr = inportb(video.crtc+1) << 8;
  546.     outportb(video.crtc, START_ADDRESS_LOW);        /* Low byte */
  547.     screen.start_addr |= inportb(video.crtc+1);
  548.  
  549.     /* count > 0 => scroll screen upwards. */
  550.     /* i is the number of scan lines scrolled */
  551.     if (count>0 && (screen.start_addr < end_of_buffer))
  552.     for(i=0;i < count;) {
  553.         if (vpel >= (int) video.char_ht) {
  554.             vpel = vpel - video.char_ht;
  555.             screen.start_addr += screen.logical_width;
  556.         }
  557.         if (vpel < 0) {
  558.             if (screen.start_addr > screen.logical_width) {
  559.                 vpel += video.char_ht;
  560.                 screen.start_addr -= screen.logical_width;
  561.             }
  562.             else vpel = 0;
  563.         }
  564.         for(;vpel< (int) video.char_ht && i<count;vpel+=speed,i+=speed) {
  565.  
  566.             /* wait for a vertical retrace */
  567.             while (!(inportb(video.isr) & 8));
  568.             /* wait for horizontal or vertical retrace */
  569.             while (inportb(video.isr) & 1);
  570.  
  571.             /* address the preset row scan register */
  572.             outport(video.crtc, (vpel << 8) | PRESET_ROW_SCAN);
  573.  
  574.             /* Reset the start address */
  575.             set_start_addr(screen.start_addr);
  576.         }
  577.     }
  578.  
  579.     /* count < 0  => scroll screen characters downwards */
  580.     /* i is the number of scan lines scrolled */
  581.     if (count < 0) {
  582.         if (vpel >= (int) video.char_ht) vpel -= video.char_ht;
  583.  
  584.         for(i=0;i < -count && screen.start_addr;) {
  585.         /* This loop determines whether to update the start address */
  586.         /* It is iterated once for each video.char_ht. */
  587.             if (vpel < 0) {
  588.                 vpel = video.char_ht + vpel;
  589.                 if (screen.start_addr > screen.logical_width)
  590.                     screen.start_addr -= screen.logical_width;
  591.                 else {
  592.                     screen.start_addr = screen.a_start;
  593.                     vpel=0;
  594.                 }
  595.             }
  596.  
  597.             /* this loop moves the screen "speed" scan lines each time through */
  598.             for(;vpel>=0 && i< -count;vpel-=speed,i+=speed) {
  599.  
  600.                 /* wait for a vertical retrace */
  601.                 while (!(inportb(video.isr) & 8));
  602.                 /* wait for horizontal or vertical retrace */
  603.                 while (inportb(video.isr) & 1);
  604.  
  605.                 /* address the preset row scan register */
  606.                 outport(video.crtc, (vpel << 8) | PRESET_ROW_SCAN);
  607.  
  608.                 /* Reset the start address */
  609.                 set_start_addr(screen.start_addr);
  610.             }
  611.         }
  612.     }
  613. }
  614.  
  615.  
  616. /* SMOOTH_PAN
  617. This function invokes smooth panning on the EGA/VGA in text mode.  The
  618. function calculates the number of scan lines per row.  The speed variable
  619. adjusts the speed in pixels per vertical retrace and takes values in the
  620. range 1-8 for EGA color and 1-9 for monochrome and the VGA.
  621. */
  622. int smooth_pan(int count, unsigned speed)
  623. {
  624.     unsigned i;
  625.  
  626.     /* count greater than zero (move viewport to the right) */
  627.     if (count>0 && !right_edge) for(i=0;i<count;) {
  628.         /* if we have scrolled a full character, reset start address */
  629.         if (hpel >= 8) {
  630.             screen.start_addr++;
  631.             set_start_addr(screen.start_addr);
  632.             if (!left_edge) if (!(screen.start_addr % (screen.logical_width)))
  633.                 right_edge = TRUE;
  634.             left_edge = FALSE;
  635.             if (!right_edge) {
  636.                 if (!(mono || vga.active)) hpel %= 8;    /* reset the pel counter */
  637.                 else {
  638.                     hpel %= 9;
  639.                     if (hpel == 8) {
  640.                         set_pel_pan(hpel);
  641.                         hpel += speed;
  642.                         hpel %= 9;
  643.                     }
  644.                 }
  645.             }
  646.             else {
  647.                 hpel = (mono || vga.active) ? 8 : 0;
  648.                 set_pel_pan(hpel);
  649.                 return (-1);
  650.             }
  651.         }
  652.         for(;hpel < 8 && i < count;i+=speed, hpel+=speed) set_pel_pan(hpel);
  653.     }
  654.  
  655.     /* count less than zero (move viewport to the left) */
  656.     else if (count < 0) {
  657.         if (mono || vga.active) {
  658.             if (left_edge && hpel == 8) return (-1);
  659.             if (hpel > 8) hpel -= 9;
  660.         }
  661.         else {
  662.             if (left_edge && hpel == 0) return (-1);
  663.             if (hpel > 7) hpel = hpel - 8;
  664.         }
  665.         for(i=0; i < (-count); ) {
  666.         /* IF WE HAVE SCROLLED A FULL CHARACTER, RESET START ADDRESS */
  667.             if (hpel<0 && !left_edge) {
  668.                 if ((mono || vga.active) && hpel == -1) {
  669.                     hpel = 8;
  670.                     set_pel_pan(hpel);
  671.                     hpel = -1 - speed;
  672.                 }
  673.                 screen.start_addr--;
  674.                 right_edge = FALSE;
  675.                 hpel = (mono || vga.active) ? 9 + hpel : 8 + hpel;
  676.                 set_start_addr(screen.start_addr);
  677.                 if (!(screen.start_addr % screen.logical_width)) {
  678.                     left_edge = TRUE;
  679.                     screen.start_addr--;
  680.                 }
  681.             }
  682.             else if (hpel<0 && left_edge) i = (-count);
  683.  
  684.             for(;hpel >= 0 && i < (-count);i+=speed, hpel-=speed) set_pel_pan(hpel);
  685.             if (left_edge && hpel < speed) {
  686.                 hpel = (mono || vga.active) ? 8 : 0;
  687.                 set_pel_pan(hpel);
  688.                 return (-1);
  689.             }
  690.         }
  691.     }
  692.     return (0);
  693. }
  694.  
  695.  
  696.  
  697. /* SET_LOGICAL_SCREEN_WIDTH defines an EGA/VGA screen width for smooth panning
  698. and sets the the global variable "screen.cols."  screen.cols must be restored
  699. when smooth panning is finished or subsequent screen writes will address the
  700. wrong screen locations.
  701. l_width is the width of the logical screen in bytes. Max 512.
  702. */
  703. unsigned set_logical_screen_width(unsigned l_width)
  704. {
  705.     l_width = (l_width > 512) ? 512 : l_width;
  706.  
  707.     /* set screen_cols */
  708.     screen.cols = l_width;
  709.  
  710.     /* set logical screen width */
  711.     l_width >>= 1;                                  /* convert to words */
  712.     outport(video.crtc, (l_width << 8) | 0x013);
  713.  
  714.     return (l_width << 1);
  715. }
  716.  
  717.  
  718. /* SET_START_ADDRESS -- Sets the start address of the screen. I.e. the  */
  719. /* address that occupies the top left of the screen.                    */
  720. void set_start_addr(unsigned start_addr)
  721. {
  722.     /* address the start address high register */
  723.     outport(video.crtc, (start_addr & 0XFF00) | START_ADDRESS_HIGH);
  724.  
  725.     /* address the start address low register */
  726.     outport(video.crtc, (start_addr << 8) | START_ADDRESS_LOW);
  727. }
  728.  
  729.  
  730. /* SET_PEL_PAN -- Sets the horizontal pel panning register to the value     */
  731. /* specified in "hpel".  Called by smooth_pan()                             */
  732. void set_pel_pan(int hpel)
  733. {
  734.     /* first wait for end of vertical retrace */
  735.     while (inportb(video.isr) & 8);
  736.  
  737.     /* wait for next vertical retrace */
  738.     while (!(inportb(video.isr) & 8));
  739.  
  740.     /* address the horizontal pel paning register */
  741.     outportb(AC_INDEX, AC_HPP);
  742.     outportb(AC_INDEX, hpel);
  743. }
  744.  
  745.  
  746. /* SET_LINE_COMPARE -- Sets the scan line at which the screen is split.     */
  747. void set_line_compare(unsigned scan_line)
  748. {
  749.     BYTE old_reg;
  750.     if (scan_line > 256) {          /* set bit 4 of the overflow register */
  751.         if (ega.active) {
  752.             if ((mode >= 4 && mode <= 7) || (mode == 0X0D || mode == 0X0E))
  753.                 outport(video.crtc, (0X11 << 8) | CRTC_OVERFLOW);
  754.             else outport(video.crtc, (0X1F << 8) | CRTC_OVERFLOW);
  755.             if (mode < 4 && !video.ecd) outport(video.crtc, (0X11 << 8) | CRTC_OVERFLOW);
  756.         }
  757.         else if (vga.active) {      /* ahhh... readable registers */
  758.             /* set bit 8 of line compare reg., which is bit 4 of the CRTC overflow reg. */
  759.             outportb(video.crtc,CRTC_OVERFLOW);
  760.             old_reg = (BYTE) inportb(video.crtc+1);
  761.             outportb(video.crtc+1,old_reg | 0X10);
  762.             /* set bit 9 of line compare reg., which is bit 6 of
  763.             the CRTC max. scan line register */
  764.  
  765.             outportb(video.crtc,MAX_SCAN_LINE);
  766.             old_reg = (BYTE) inportb(video.crtc+1);
  767.             if (old_reg | 0X40) outportb(video.crtc+1,old_reg & 0XBF);
  768.         }
  769.         scan_line -= 256;
  770.     }
  771.     else {
  772.         if (ega.active) {
  773.             /* clear bit 8 of the line compare register (which is stored as bit */
  774.             /* 4 of the CRTC overflow register).  This register is write only.  */
  775.             if (!video.ecd && mode < 4) outport(video.crtc, (1 << 8) | CRTC_OVERFLOW);
  776.             else if (video.ecd && mode < 4) outport(video.crtc, (0XF << 8) | CRTC_OVERFLOW);
  777.             if ((mode >= 4 && mode < 7) || (mode == 0XD || mode == 0XE) ) outport(video.crtc, (1 << 8) | CRTC_OVERFLOW);
  778.             if (mode == 7 || mode > 0X0E) outport(video.crtc, (0XF << 8) | CRTC_OVERFLOW);
  779.         }
  780.         if (vga.active) {
  781.             outportb(video.crtc,CRTC_OVERFLOW); /* clear bit 8 of line compare reg. */
  782.             old_reg = (BYTE) inportb(video.crtc+1);
  783.             outportb(video.crtc,((old_reg & 0XEF) << 8) | CRTC_OVERFLOW);
  784.             outportb(video.crtc,MAX_SCAN_LINE);  /* clear bit 9 of line compare reg. */
  785.             old_reg = (BYTE) inportb(video.crtc+1);
  786.             outportb(video.crtc,((old_reg & 0XBF) << 8) | MAX_SCAN_LINE);
  787.         }
  788.     }
  789.     outport(video.crtc, (scan_line << 8) | LINE_COMPARE);
  790. }
  791.  
  792.  
  793. /************************** UTILITY FUNCTIONS *********************************/
  794.  
  795.  
  796. /* LOAD_EGA_COLOR -- Load an EGA or VGA palette register with a selected color */
  797. void load_ega_color(BYTE pregister, BYTE color)
  798. {
  799.     union REGS regs;
  800.  
  801.     regs.x.ax = 0x1000;     /* Service 0. Set an individual palette register */
  802.     regs.h.bl = pregister;  /* BL holds the register to be set (0-15) */
  803.     regs.h.bh = color;      /* BH hols the new color */
  804.     int86(VIDEO, ®s, ®s);
  805. }
  806.  
  807.  
  808. /* SET_VIDEO_MODE -- sets the active video mode. */
  809. unsigned set_video_mode(BYTE m)
  810. {
  811.     union REGS regs;
  812.  
  813.     regs.h.ah = 0;          /* service 0 - set current video mode */
  814.     regs.h.al = m;          /* requested mode # */
  815.     int86(VIDEO, ®s, ®s);
  816.  
  817.     return m;
  818. }
  819.  
  820.  
  821. /* GET_V_MODE -- Returns the video mode and sets the global variables       */
  822. /* screen.cols (# of screen columns), screen.rows (# of screen rows)        */
  823. /* video.base (screen buffer starting address), and mono.                   */
  824. int get_v_mode(void)
  825. {
  826.     union REGS regs;
  827.  
  828.     regs.h.ah = 0xf;            /* get current video mode */
  829.     int86(VIDEO, ®s, ®s);
  830.     mode = regs.h.al;           /* current mode # */
  831.     screen.cols = regs.h.ah;
  832.     regs.h.dl = 0x18;           /* dummy #rows for CGA compatibility */
  833.     regs.h.bh = 0;
  834.     regs.x.ax = 0x1130;         /* BIOS function 0x11, sub-function 0x30 */
  835.     int86(VIDEO,®s,®s);
  836.     screen.rows = regs.h.dl + 1;/* screen rows returned in DL */
  837.     if (mode == 7) {
  838.         video.base = (BYTE far *) 0XB0000000L;
  839.         mono = TRUE;
  840.     }
  841.     else {
  842.         video.base = (BYTE far *) 0XB8000000L;
  843.         mono = FALSE;
  844.     }
  845.  
  846.     return (mode);
  847. }
  848.  
  849.  
  850. /* GET_V_CONFIG -- A portmanteau routine to determine if an EGA, VGA or     */
  851. /* neither is present and, if an EGA or VGA is present, to set the          */
  852. /* following video parameters:                                              */
  853. /* vga.present = TRUE if a VGA is present in the system.                    */
  854. /* vga.active = TRUE if the VGA is the active adapter.                      */
  855. /* ega.present = TRUE if an EGA is present in the system.                   */
  856. /* ega.active = TRUE if an EGA is the active adapter.                       */
  857. /* video.ram = amount of video RAM installed on the adapter.                */
  858. /* video.color = TRUE if the EGA/VGA drives a color display.                */
  859. /* video.ecd = TRUE if the EGA/VGA drives an enhanced display.              */
  860. /* video.ev_active = TRUE if the EGA or the VGA is active.                  */
  861. /* This function should be preceeded by a call to get_v_mode().             */
  862. void get_v_config(void)
  863. {
  864.  
  865.     union REGS regs;
  866.     BYTE e_byte;           /* EGA information byte in ROM data area */
  867.  
  868.     /* First, check for the presence of an VGA */
  869.     regs.x.ax = 0x1a00;     /* Function call 0x1a -- Tech. Ref./2 p. 3-24 */
  870.     int86(VIDEO, ®s, ®s);         /* Function supported => VGA */
  871.     if (regs.h.al == 0x1a) {
  872.         vga.present = TRUE;
  873.         if (regs.h.bl < 7) vga.active = FALSE;
  874.         else vga.active = TRUE;
  875.     }
  876.     else {
  877.         vga.present = FALSE;
  878.         vga.active = FALSE;
  879.     }
  880.     /* Check for the presence of an EGA and set relevant EGA/VGA parameters */
  881.     regs.h.ah = 0x12;       /* EGA BIOS alternate select. Tech. Ref. p106. */
  882.     regs.h.bl = 0x10;       /* return EGA information.              */
  883.     int86(VIDEO, ®s, ®s);
  884.     if (regs.h.bl == 0x10 || vga.present) ega.present = FALSE;
  885.     else ega.present = TRUE;
  886.  
  887.     /* If an EGA or VGA is present then set some important parameters */
  888.     if (ega.present || vga.present) {   /* EGA/VGA is present -- is it active? */
  889.         video.ram = regs.h.bl*64 + 64;  /* EGA/VGA installed memory */
  890.         if (regs.h.bh) video.color = FALSE; /* EGA/VGA drives a mono monitor */
  891.         else video.color = TRUE;            /* EGA/VGA drives a color monitor */
  892.  
  893.         /* See if EGA/VGA drives an Enhanced Color Display */
  894.         if (video.color) {
  895.             if (regs.h.cl == 3 || regs.h.cl == 9) video.ecd = TRUE;
  896.             else video.ecd = FALSE;
  897.         }
  898.         e_byte = peekb(0,0x487);                    /* EGA info. byte */
  899.         if (e_byte & 8) video.ev_active = FALSE;    /* EGA not active */
  900.         else {
  901.             video.ev_active = TRUE;                 /* EGA is active */
  902.             if (!vga.active) ega.active = TRUE;
  903.         }
  904.     }
  905.  
  906.     if (vga.active) {
  907.         regs.x.ax = 0X1202;
  908.         regs.h.bl = 0X30;
  909.         int86(VIDEO, ®s, ®s);
  910.     }
  911.     /* check # of screen rows for font size */
  912.     if (vga.active) video.char_ht = (BYTE) (400/screen.rows);
  913.     else if (ega.active) if (video.ecd || mono) video.char_ht = (BYTE) (350/screen.rows);
  914.     else video.char_ht = (BYTE) (200/screen.rows);
  915. }
  916.  
  917.  
  918. /* GET_KEY -- Returns the scan code of the next key in the keyboard buffer. */
  919. unsigned get_key(void)
  920. {
  921.     union REGS regs;
  922.  
  923.     regs.h.ah = 0;     /* read next keyboard character */
  924.     int86(KEYBOARD, ®s, ®s);
  925.     return ((unsigned) regs.h.ah);
  926. }
  927.  
  928.  
  929. /* GET_CURSOR_ SIZE -- Get the cursor size on the active page.         */
  930. int get_cursor_size(void)
  931. {
  932.     union REGS regs;
  933.     int hi,lo;
  934.  
  935.     regs.h.ah = 3;                 /* request cursor size */
  936.     regs.h.bh = 0;                 /* from page 0 */
  937.     int86(VIDEO, ®s, ®s);    /* ROM BIOS video service */
  938.     hi = regs.h.ch;                /* top scan line of cursor */
  939.     lo = regs.h.cl;                /* bottom scan line of cursor */
  940.     return (hi << 8) | lo;         /* combine for return */
  941. }
  942.  
  943.  
  944. /* SET CURSOR SIZE sets the cursor size. top_line=bottom_line=0 turns it off           */
  945. void set_cursor_size(BYTE top_line, BYTE bottom_line)
  946. {
  947.     union REGS regs;
  948.  
  949.     regs.h.ah = 1;                      /* BIOS function 1, set cursor size */
  950.     if (top_line == 0 && bottom_line == 0) regs.h.ch = 32; /* request cursor off */
  951.     else {
  952.         regs.h.ch = top_line;               /* row */
  953.         regs.h.cl = bottom_line;            /* column */
  954.     }
  955.     int86(VIDEO, ®s, ®s);
  956. }
  957.  
  958.  
  959. /* ERROR -- A simple error-handler */
  960. void error(char *fs,...)
  961. {
  962.     va_list argptr;
  963.     va_start(argptr,fs);
  964.     vfprintf(stderr,fs,argptr);
  965.     va_end(argptr);
  966.     exit(1);
  967. }
  968.  
  969.  
  970. #if defined(__WATCOMC__)
  971. /*
  972. WATCOM C does not access the DOS file system in a fashion similar to the other
  973. compilers so we write our own "findfirst()" function.  WATCOM C uses a
  974. function called opendir(), which could be used instead.
  975. */
  976.  
  977. /* NOTE: THE FOLLOWING IMPLEMENTATION IS VALID ONLY IN SMALL DATA MODELS */
  978. int findfirst(const char *pathname, struct ffblk *ffblk, int attrib)
  979. {
  980.     union REGS regs;
  981.     struct SREGS sregs;
  982.  
  983.     segread(&sregs);
  984.     regs.h.ah = 0X1A;               /* DOS set DTA function         */
  985.     regs.x.dx = (unsigned) ffblk;   /* address of new DTA address   */
  986.     intdosx(®s, ®s, &sregs);  /* this reads the registers...  */
  987.  
  988.     regs.h.ah = 0X4E;               /* DOS find first matching file */
  989.     regs.x.cx = attrib;             /* search attribute             */
  990.     regs.x.dx = (unsigned) pathname;/* DS:DX is address of pathname */
  991.     intdosx(®s, ®s, &sregs);
  992.     return (regs.x.ax);
  993. }
  994. #endif
  995.  
  996. [LISTING 2]
  997.  
  998.  
  999. /*
  1000. SMOOTHLIB--A library of C language routines to do smooth scrolling and panning on the EGA and the VGA.
  1001. by Andrew J. Chalk.
  1002. SYSTEM REQUIREMENTS: EGA or VGA. Enhanced Color, Monochrome or Analog Display.
  1003. COMPILER SUPPORT: MSC 5.0+/Quick C
  1004. Simple compiler invocation (suggested):
  1005.     MSC:            "CL /AS sb.c"
  1006. First version: 3/5/88.  Last update 09-01-88.
  1007. */
  1008.  
  1009. #include <conio.h>
  1010. #include <stdio.h>
  1011.  
  1012.  
  1013.  
  1014. typedef unsigned char BYTE;     /* A convenient new data type */
  1015.  
  1016.  
  1017.  
  1018. /* MANIFEST CONSTANTS */
  1019. #define LOGICAL_WIDTH 132   /* EGA/VGA logical screen width (bytes, max. 512) */
  1020. #define TRUE            1
  1021. #define FALSE       !TRUE
  1022.  
  1023. /* Macros to make a far pointer and examine at a byte in memory */
  1024. #define MK_FP(seg,ofs)  ((void far *)((((unsigned long)(seg)) << 16) | (ofs)))
  1025. #define peekb(a,b)      (*((char far*)MK_FP((a),(b))))
  1026.  
  1027.  
  1028.  
  1029. /* EGA AND VGA REGISTER VALUES */
  1030. #define PRESET_ROW_SCAN     8     /* Address of preset row scan reg. of CRTC */
  1031. #define START_ADDRESS_HIGH  0X0C  /* Address of start address h reg. of CRTC */
  1032. #define START_ADDRESS_LOW   0X0D  /* Address of start address l reg. of CRTC */
  1033. #define AC_INDEX            0X3C0 /* Attribute Controller Index Register     */
  1034. #define AC_HPP              0X13 | 0X20   /* Horizontal Pel Panning Register */
  1035. #define AC_MCR              0X10  /* Attribute Controller Mode Control Reg.  */
  1036. #define LINE_COMPARE        0X18  /* CRTC line compare register.             */
  1037. #define CRTC_OVERFLOW       0X07  /* CRTC overflow register.                 */
  1038. #define MAX_SCAN_LINE       0X09  /* CRTC maximum scan line register.        */
  1039.  
  1040.  
  1041.  
  1042. /* GLOBAL VARIABLES */
  1043. unsigned end_of_buffer;         /* address of the end of the video buffer   */
  1044. unsigned right_edge, left_edge; /* TRUE if we are at the edge of the screen */
  1045. int vpel, hpel;                 /* vertical pel height, etc.                */
  1046. int mode, mono;                 /* video mode, monochrome (TRUE or FALSE)   */
  1047.  
  1048.  
  1049.  
  1050. /* STRUCTURE DEFINITIONS
  1051. The following four structures define convenients form in which to store
  1052. video information and can be extended with additional members.
  1053. We will declare one of each type (below).
  1054. */
  1055. struct video_descriptor {
  1056.     unsigned ev_active;             /* if TRUE, an EGA or VGA is active     */
  1057.     unsigned color;                 /* if TRUE, ega drives color monitor    */
  1058.     unsigned ecd;                   /* if TRUE, Enhanced Color Display      */
  1059.     unsigned ram;                   /* size of EGA memory (in k)            */
  1060.     BYTE char_ht, char_wi;          /* character height and width (EGA/VGA) */
  1061.     BYTE far *base;                 /* start address of the video buffer    */
  1062.     unsigned address;               /* start addr. of the active video page */
  1063.     unsigned isr;                   /* EGA/VGA input status register address*/
  1064.     unsigned crtc;                  /* CRT Controller Register address      */
  1065. };
  1066.  
  1067.  
  1068. /* We place all the information related to the screen in one structure */
  1069. struct screen_descriptor {
  1070.     unsigned rows;
  1071.     unsigned cols;
  1072.     unsigned a_start;      /* start address of screen A in split screen mode */
  1073.     unsigned logical_width;     /* screen logical width in words. max: 0x100 */
  1074.     unsigned start_addr;        /* screen start address. max: 0X4000 */
  1075. };
  1076.  
  1077.  
  1078. /* This structure contains information specific to the EGA */
  1079. struct enhanced_graphics_adapter {
  1080.     unsigned present;               /* if TRUE, EGA present */
  1081.     unsigned active;                /* if TRUE, EGA active */
  1082. };
  1083.  
  1084.  
  1085. /* This structure contains information specific to the VGA */
  1086. struct video_graphics_array {
  1087.     unsigned present;               /* if TRUE, VGA present */
  1088.     unsigned active;                /* if TRUE, EGA present */
  1089.  
  1090. };
  1091.  
  1092.  
  1093. /* STRUCTURE DECLARATIONS */
  1094. struct video_descriptor video;
  1095. struct screen_descriptor screen;
  1096. struct enhanced_graphics_adapter ega;
  1097. struct video_graphics_array vga;
  1098.  
  1099.  
  1100.  
  1101. /* FUNCTION PROTOTYPES -- In order of appearance */
  1102. void        smooth_scroll(int count, unsigned speed);
  1103. int         smooth_pan(int count, unsigned speed);
  1104. unsigned    set_logical_screen_width(unsigned l_width);
  1105. void        set_start_addr(unsigned start_addr);
  1106. void        set_pel_pan(int hpel);
  1107.  
  1108.  
  1109.  
  1110. /*** FUNCTION DEFINITIONS BEGIN HERE ***/
  1111.  
  1112. /*
  1113. SMOOTH_SCROLL scrolls the EGA/VGA video buffer in text mode the number of
  1114. scan lines indicated in "count" at a speed of "speed" scan lines per
  1115. vertical retrace.
  1116. */
  1117. void smooth_scroll(int count, unsigned speed)
  1118. {
  1119.  
  1120.     unsigned i;
  1121.  
  1122.     /* GET THE START ADDRESS OF THE SCREEN BUFFER */
  1123.     outp(video.crtc, START_ADDRESS_HIGH);       /* High byte */
  1124.     screen.start_addr = inp(video.crtc+1) << 8;
  1125.     outp(video.crtc, START_ADDRESS_LOW);        /* Low byte */
  1126.     screen.start_addr |= inp(video.crtc+1);
  1127.  
  1128.     /* COUNT > 0 => SCROLL SCREEN UPWARDS. */
  1129.     /* i is the number of scan lines scrolled */
  1130.     if (count>0 && (screen.start_addr < end_of_buffer))
  1131.     for(i=0;i < count;) {
  1132.         if (vpel >= (int) video.char_ht) {
  1133.             vpel = vpel - video.char_ht;
  1134.             screen.start_addr += screen.logical_width;
  1135.         }
  1136.         if (vpel < 0) {
  1137.             if (screen.start_addr > screen.logical_width) {
  1138.                 vpel += video.char_ht;
  1139.                 screen.start_addr -= screen.logical_width;
  1140.             }
  1141.             else vpel = 0;
  1142.         }
  1143.         for(;vpel< (int) video.char_ht && i<count;vpel+=speed,i+=speed) {
  1144.  
  1145.             /* wait for a vertical retrace */
  1146.             while (!(inp(video.isr) & 8));
  1147.             /* wait for horizontal or vertical retrace */
  1148.             while (inp(video.isr) & 1);
  1149.  
  1150.             /* address the preset row scan register */
  1151.             outpw(video.crtc, (vpel << 8) | PRESET_ROW_SCAN);
  1152.  
  1153.             /* Reset the start address */
  1154.             set_start_addr(screen.start_addr);
  1155.         }
  1156.     }
  1157.  
  1158.     /* COUNT < 0  => SCROLL SCREEN CHARACTERS DOWNWARDS */
  1159.     /* i is the number of scan lines scrolled */
  1160.     if (count < 0) {
  1161.         if (vpel >= (int) video.char_ht) vpel -= video.char_ht;
  1162.  
  1163.         for(i=0;i < -count && screen.start_addr;) {
  1164.         /* This loop determines whether to update the start address */
  1165.         /* It is iterated once for each video.char_ht. */
  1166.             if (vpel < 0) {
  1167.                 vpel = video.char_ht + vpel;
  1168.                 if (screen.start_addr > screen.logical_width)
  1169.                     screen.start_addr -= screen.logical_width;
  1170.                 else {
  1171.                     screen.start_addr = screen.a_start;
  1172.                     vpel=0;
  1173.                 }
  1174.             }
  1175.  
  1176.             /* this loop moves the screen "speed" scan lines each time through */
  1177.             for(;vpel>=0 && i< -count;vpel-=speed,i+=speed) {
  1178.  
  1179.                 /* wait for a vertical retrace */
  1180.                 while (!(inp(video.isr) & 8));
  1181.                 /* wait for horizontal or vertical retrace */
  1182.                 while (inp(video.isr) & 1);
  1183.  
  1184.                 /* address the preset row scan register */
  1185.                 outpw(video.crtc, (vpel << 8) | PRESET_ROW_SCAN);
  1186.  
  1187.                 /* Reset the start address */
  1188.                 set_start_addr(screen.start_addr);
  1189.             }
  1190.         }
  1191.     }
  1192. }
  1193.  
  1194.  
  1195.  
  1196. /*
  1197. SMOOTH_PAN -- This function invokes smooth panning on the EGA/VGA in
  1198. text mode.  The function calculates the number of scan lines per row.
  1199. The speed variable adjusts the speed in pixels per vertical retrace and
  1200. takes values in the range 1-8 for EGA color and 1-9 for monochrome and
  1201. the VGA.
  1202. */
  1203. int smooth_pan(int count, unsigned speed)
  1204. {
  1205.     unsigned i;
  1206.  
  1207.     /* count greater than zero (move viewport to the right) */
  1208.     if (count>0 && !right_edge) for(i=0;i<count;) {
  1209.         /* if we have scrolled a full character, reset start address */
  1210.         if (hpel >= 8) {
  1211.             screen.start_addr++;
  1212.             set_start_addr(screen.start_addr);
  1213.             if (!left_edge) if (!(screen.start_addr % (screen.logical_width)))
  1214.                 right_edge = TRUE;
  1215.             left_edge = FALSE;
  1216.             if (!right_edge) {
  1217.                 if (!(mono || vga.active)) hpel %= 8;    /* reset the pel counter */
  1218.                 else {
  1219.                     hpel %= 9;
  1220.                     if (hpel == 8) {
  1221.                         set_pel_pan(hpel);
  1222.                         hpel += speed;
  1223.                         hpel %= 9;
  1224.                     }
  1225.                 }
  1226.             }
  1227.             else {
  1228.                 hpel = (mono || vga.active) ? 8 : 0;
  1229.                 set_pel_pan(hpel);
  1230.                 return (-1);
  1231.             }
  1232.         }
  1233.         for(;hpel < 8 && i < count;i+=speed, hpel+=speed) set_pel_pan(hpel);
  1234.     }
  1235.  
  1236.     /* count less than zero (move viewport to the left) */
  1237.     else if (count < 0) {
  1238.         if (mono || vga.active) {
  1239.             if (left_edge && hpel == 8) return (-1);
  1240.             if (hpel > 8) hpel -= 9;
  1241.         }
  1242.         else {
  1243.             if (left_edge && hpel == 0) return (-1);
  1244.             if (hpel > 7) hpel = hpel - 8;
  1245.         }
  1246.         for(i=0; i < (-count); ) {
  1247.         /* IF WE HAVE SCROLLED A FULL CHARACTER, RESET START ADDRESS */
  1248.             if (hpel<0 && !left_edge) {
  1249.                 if ((mono || vga.active) && hpel == -1) {
  1250.                     hpel = 8;
  1251.                     set_pel_pan(hpel);
  1252.                     hpel = -1 - speed;
  1253.                 }
  1254.                 screen.start_addr--;
  1255.                 right_edge = FALSE;
  1256.                 hpel = (mono || vga.active) ? 9 + hpel : 8 + hpel;
  1257.                 set_start_addr(screen.start_addr);
  1258.                 if (!(screen.start_addr % screen.logical_width)) {
  1259.                     left_edge = TRUE;
  1260.                     screen.start_addr--;
  1261.                 }
  1262.             }
  1263.             else if (hpel<0 && left_edge) i = (-count);
  1264.  
  1265.             for(;hpel >= 0 && i < (-count);i+=speed, hpel-=speed) set_pel_pan(hpel);
  1266.             if (left_edge && hpel < speed) {
  1267.                 hpel = (mono || vga.active) ? 8 : 0;
  1268.                 set_pel_pan(hpel);
  1269.                 return (-1);
  1270.             }
  1271.         }
  1272.     }
  1273.     return (0);
  1274. }
  1275.  
  1276.  
  1277.  
  1278. /*
  1279. SET_LOGICAL_SCREEN_WIDTH defines an EGA/VGA screen width (in bytes) for
  1280. smooth panning and sets the the global variable "screen.cols."
  1281. screen.cols is the number of screen columns and must be restored when
  1282. smooth panning is finished or subsequent screen writes will address the
  1283. wrong screen locations.
  1284. l_width is the width of the logical screen in bytes. Max 512.
  1285. */
  1286. unsigned set_logical_screen_width(unsigned l_width)
  1287. {
  1288.     l_width = (l_width > 512) ? 512 : l_width;
  1289.  
  1290.     /* set screen_columns */
  1291.     screen.cols = l_width;
  1292.  
  1293.     /* set logical screen width */
  1294.     l_width >>= 1;                                  /* convert to words */
  1295.     outpw(video.crtc, (l_width << 8) | 0x013);
  1296.  
  1297.     return (l_width << 1);
  1298. }
  1299.  
  1300.  
  1301.  
  1302. /*
  1303. SET_START_ADDRESS -- Sets the start address of the screen. I.e. the
  1304. address that occupies the top left of the screen.
  1305. */
  1306. void set_start_addr(unsigned start_addr)
  1307. {
  1308.     /* address the start address high register */
  1309.     outpw(video.crtc, (start_addr & 0XFF00) | START_ADDRESS_HIGH);
  1310.  
  1311.     /* address the start address low register */
  1312.     outpw(video.crtc, (start_addr << 8) | START_ADDRESS_LOW);
  1313. }
  1314.  
  1315.  
  1316.  
  1317. /*
  1318. SET_PEL_PAN -- Sets the horizontal pel panning register to the value
  1319. specified in "hpel".  Called by smooth_pan()
  1320. */
  1321. void set_pel_pan(int hpel)
  1322. {
  1323.     /* first wait for end of vertical retrace */
  1324.     while (inp(video.isr) & 8);
  1325.  
  1326.     /* wait for next vertical retrace */
  1327.     while (!(inp(video.isr) & 8));
  1328.  
  1329.     /* address the horizontal pel paning register */
  1330.     outp(AC_INDEX, AC_HPP);
  1331.     outp(AC_INDEX, hpel);
  1332. }
  1333.  
  1334.  
  1335.